package com.gddb.xwj.common.thinks;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * @Author xuwj
 * @Description: 集合排序通用程序, 假设给定排序条件 Q asc, W desc, E asc 要求对集合Z按上述字段做排序
 * @CreateTime 2020/1/8 23:36
 * @Version 1.0
 */
@Slf4j
public class CustomListComparator implements Comparator<Object> {
    //排序条件列表
    private List<String> sort;
    //存放字段的map
    private Map<String, Field> map = new HashMap<>();
    //存放排序条件的map
    Map<String, String> sortMap = new HashMap<>();

    /*
    将条件放入排序map中
    排序集合
    是否反序，如有的情况需要根据字符串末尾条件先排序
    */
    public CustomListComparator(List<String> sort, boolean reverse) throws IllegalAccessException {
        this.sort = sort;
        if (reverse) {
            Collections.reverse(this.sort);//反序
        }
        for (String s : sort) {
            String[] sortArray = s.split(" ");
            if (sortArray != null && sortArray.length == 2) {
                String sidx = sortArray[0];//字段
                String sord = sortArray[1];//排序顺序
                sortMap.put(sidx, sord);
            }
        }
    }


    /*获取所有属性值*/
    private void getFields(Object o1, Object o2, Map<String, Object> map1, Map<String, Object> map2) throws IllegalAccessException {
        if (map.isEmpty()) {
            Class<?> aClass = o1.getClass();
            while (aClass != null) {
                Field[] declareFields = aClass.getDeclaredFields();
                for (Field declareField : declareFields) {
                    String name = declareField.getName();
                    if (StringUtils.isNotEmpty(sortMap.get(name))) {
                        map.put(name, declareField);
                        declareField.setAccessible(true);
                        map1.put(name, declareField.get(o1));
                        map2.put(name, declareField.get(o2));
                    }
                }
                aClass = aClass.getSuperclass();
            }

        } else {
            Set<Map.Entry<String, Field>> entries = map.entrySet();
            for (Map.Entry<String, Field> entry : entries) {
                Field field = entry.getValue();
                String name = entry.getKey();
                map1.put(name, field.get(o1));
                map2.put(name, field.get(o2));
            }
        }
    }

    @Override
    public int compare(Object o1, Object o2) {
        int compareResult = 0;
        if (sort != null && !sort.isEmpty()) {
            Map<String, Object> allFields1 = new HashMap<>();
            Map<String, Object> allFields2 = new HashMap<>();
            try {
                getFields(o1, o2, allFields1, allFields2);
            } catch (IllegalAccessException e) {
                log.warn("自定义排序反射获取异常", e);
            }
            if (allFields1 == null || null == allFields2) {
                return 0;
            }
            for (String s : sort) {
                String[] sortArray = s.split(" ");
                if (sortArray != null && sortArray.length == 2) {
                    String sidx = sortArray[0];//字段
                    String sord = sortArray[1];//排序顺序
                    try {
                        Object value1 = allFields1.get(sidx);
                        Object value2 = allFields2.get(sidx);
                        if ((value1 == null && null == value2) || (value1 == "" && "" == value2)) continue;
                        else if (null == value2 || "" == value2) return -1;
                        else if (null == value1 || "" == value1) return 1;
                        if (value1 instanceof String) {
                            String str1 = (String) value1;
                            String str2 = (String) value2;
                            if (StringUtils.isNumeric(str1) && StringUtils.isNumeric(str2)) {
                                compareResult = Integer.valueOf(str1) - Integer.valueOf(str2);
                            } else {
                                compareResult = str1.compareTo(str2);
                            }
                        } else if (value1 instanceof Date) {
                            compareResult = ((Date) value1).compareTo((Date) value2);
                        } else if (value1 instanceof BigDecimal) {
                            compareResult = ((BigDecimal) value1).compareTo((BigDecimal) value2);
                        }
                        if (compareResult == 0) {
                            continue;
                        } else {
                            if ("asc".equalsIgnoreCase(sord)) return compareResult > 0 ? 1 : -1;
                            else if ("desc".equalsIgnoreCase(sord)) return compareResult > 0 ? -1 : 1;
                        }

                    } catch (Exception e) {
                        log.warn("自定义排序异常", e);
                    }
                }
            }
        }
        return compareResult;
    }

    //主函数入口
    public static void main(String[] args) throws IllegalAccessException {
        String strSort = "time desc,productCode asc";
        List<String> sorts = Arrays.asList(strSort.split(","));
        List<TestEntity> logs = new ArrayList<>();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        long l = System.currentTimeMillis();
        //伪造100个对象测试
        for (int i = 0; i < 100; i++) {
            TestEntity s1 = new TestEntity();
            if (i % 10 == 0) {
                s1.setProductCode(0 + "");
            } else {
                s1.setProductCode(i + "");
            }
//            String strTime = sdf.format(new Date(1 + (long) i * 1000 * 60 * 60));
//            Date dTtime =sdf.parse(strTime);
            s1.setTime(new Timestamp((new Date(1 + (long) i * 1000 * 60 * 60)).getTime()));
            logs.add(s1);
        }
        //开始时间
        Collections.sort(logs, new CustomListComparator(sorts, true));
        System.out.println(System.currentTimeMillis() - l);

        for (TestEntity log : logs) {
            System.out.println(log.getProductCode() + " ### " + log.getTime());
        }
    }


}
